unit AltList;

interface

type

  TAltList<T: class> = class
  private
    FreeIndex: array of Integer;
    VirtualIndex: array of Integer;
    Item: TArray<T>;
    fCount: Integer;
    function IndexValid(Index: Integer): Boolean;
    function Get(Index: Integer): T;
    procedure Put(Index: Integer; const S: T);
    function NewIndex: Integer;
  public
    procedure Assign(Source: TAltList<T>);
    constructor Create;
    destructor Destroy; override;
    function Add(S: T): Integer; overload;
    function Add: Integer; overload;
    function Add(S: T; ReturnItem: Boolean): T; overload;
    function Add(ReturnItem: Boolean): T; overload;
    procedure Move(A, B: Integer);
    procedure AddAltList(AltList: TAltList<T>);
    procedure Remove(Index: Integer; FreeItem: Boolean = True);
    procedure Replace(S: T; Index: Integer);
    function Insert(S: T; Index: Integer): Integer;
    procedure Clear;
    property Values[Index: Integer]: T read Get write Put; default;
    property Count: Integer read fCount;
  end;


implementation

{ TAltList<T> }

function TAltList<T>.Add(S: T): Integer;
var
  loc_RealIndex: Integer;
begin
  SetLength(VirtualIndex, Count + 1);
  loc_RealIndex := NewIndex;
  Item[loc_RealIndex] := S;
  VirtualIndex[Count] := loc_RealIndex;
  Result := Count;
  inc(fCount);
end;

function TAltList<T>.Add: Integer;
begin
  Result := Add(nil);
end;

function TAltList<T>.Add(S: T; ReturnItem: Boolean): T;
begin
  Result := Self[Add(S)];
end;

function TAltList<T>.Add(ReturnItem: Boolean): T;
begin
  Result := Self[Add];
end;

procedure TAltList<T>.AddAltList(AltList: TAltList<T>);
var
  i: Integer;
begin
  for i := 0 to AltList.Count - 1 do
    Add(AltList[i]);
end;

procedure TAltList<T>.Assign(Source: TAltList<T>);
begin
  Clear;
  AddAltList(Source);
end;

procedure TAltList<T>.Clear;
var
  i: Integer;
begin
  for i := 0 to Count - 1 do
    Values[i].Free;
  fCount := 0;
  SetLength(FreeIndex, 0);
  SetLength(VirtualIndex, 0);
  SetLength(Item, 0);
end;

constructor TAltList<T>.Create;
begin
  inherited Create;
  fCount := 0;
  Clear;
end;

destructor TAltList<T>.Destroy;
begin
  Clear;
  inherited Destroy;
end;

function TAltList<T>.Get(Index: Integer): T;
begin
  if IndexValid(Index) then
    Result := Item[VirtualIndex[Index]]
  else
    Result := T(nil);
end;

function TAltList<T>.IndexValid(Index: Integer): Boolean;
begin
  Result := Index in [0 .. Count - 1];
end;

function TAltList<T>.Insert(S: T; Index: Integer): Integer;
var
  loc_RealIndex, i: Integer;
begin
  Result := -1;
  if IndexValid(Index) then
  begin
    SetLength(VirtualIndex, Count + 1);
    for i := Count downto Index + 1 do
      VirtualIndex[i] := VirtualIndex[i - 1];
    loc_RealIndex := NewIndex;
    Item[loc_RealIndex] := S;
    VirtualIndex[Index] := loc_RealIndex;
    Result := Index;
    inc(fCount);
  end
end;

procedure TAltList<T>.Move(A, B: Integer);
var
  C: Integer;
begin
  if (IndexValid(A)) and (IndexValid(B)) then
  begin
    C := VirtualIndex[A];
    VirtualIndex[A] := VirtualIndex[B];
    VirtualIndex[B] := C;
  end;
end;

function TAltList<T>.NewIndex: Integer;
var
  loc_LenFreeIndex: Integer;
begin
  loc_LenFreeIndex := length(FreeIndex);
  if loc_LenFreeIndex > 0 then
  begin
    Result := FreeIndex[loc_LenFreeIndex - 1];
    SetLength(FreeIndex, loc_LenFreeIndex - 1);
  end
  else
  begin
    Result := Count;
    SetLength(Item, Result + 1);
  end;
end;

procedure TAltList<T>.Put(Index: Integer; const S: T);
begin
  if Index = Count then
    Add(S)
  else if IndexValid(Index) then
    Replace(S, Index)
  else if Index = -1 then
    Insert(S, 0);
end;

procedure TAltList<T>.Remove(Index: Integer; FreeItem: Boolean);
var
  loc_LenFreeIndex, loc_LenVirtualIndex, i: Integer;
begin
  if IndexValid(Index) then
  begin
    if FreeItem then
      Values[Index].Free;
    Values[Index] := nil;
    loc_LenFreeIndex := length(FreeIndex);
    SetLength(FreeIndex, loc_LenFreeIndex + 1);
    FreeIndex[loc_LenFreeIndex] := VirtualIndex[Index];
    loc_LenVirtualIndex := length(VirtualIndex);
    for i := Index to loc_LenVirtualIndex - 1 do
      VirtualIndex[i] := VirtualIndex[i + 1];
    SetLength(VirtualIndex, loc_LenVirtualIndex - 1);
    dec(fCount);
  end;
end;

procedure TAltList<T>.Replace(S: T; Index: Integer);
begin
  if IndexValid(Index) then
  begin
    if Values[Index] <> nil then
      Values[Index].Free;
    Values[Index] := S;
  end;
end;

end.
